Skip to content

AMT coverage: g6-cli-plumbing-output-enco - #197

Merged
thedavidmeister merged 3 commits into
mainfrom
2026-08-22-amt-g6-cli-plumbing-output-enco
Aug 25, 2026
Merged

thedavidmeister merged 3 commits into
mainfrom
2026-08-22-amt-g6-cli-plumbing-output-enco

Conversation

@thedavidmeister

@thedavidmeister thedavidmeister commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

Adversarial mutation-test coverage for group g6-cli-plumbing-output-enco (30 behaviours across the CLI plumbing/dispatch, error enum, rain-metaboard-subgraph client, bindings crate, and the LibMeta / LibIMetaBoardV1_2 / LibDescribedByMeta Solidity units), at scanned commit bba50a7.

Probe tool: nix run github:rainlanguage/adversarial-mutation-test#mutation-probe over exact-string mutants; suites are the CI ground truth (nix develop -c cargo test, nix develop -c forge test). Mutants files were never committed. All kills below are proven by the probe with named killer tests; PREEXISTING kills were attributed in a first pass run before any new test was written.

Pre-existing test defect fixed (baseline)

Adding the magic literal hex"ff0a89c674ee7874" to a test file seeded forge's fuzz dictionary and immediately falsified three pre-existing fuzz oracles on UNMUTATED code (counterexample data = 0xff0a89c674ee7874): testIsRainMetaV1_2Fuzz, testCheckMetaUnhashedV1_2Fuzz and testCheckMetaHashedV1_2BadMagicGoodHash all asserted that raw fuzz data is never rain meta, which is wrong by spec whenever the data itself begins with the magic. The library is correct; the oracles are fixed in place to branch on the prefix (no assertion was weakened — the magic-prefixed branch now asserts the spec-correct expectation instead of a false one). Filed as #184.

Behaviour matrix

# Behaviour Mutation Verdict Killing test
1 dispatch Build arm D1 arm no-ops to Ok(()) KILLED (new) test_dispatch_build
2 dispatch Solc arm D2 arm no-ops KILLED (new) test_dispatch_solc_artifact
3 dispatch Subgraph arm D3 arm no-ops KILLED (new) test_dispatch_subgraph
4 dispatch Magic arm D4 arm no-ops KILLED (new) test_dispatch_magic_ls
5 dispatch Schema arm D5 arm no-ops KILLED (new) test_dispatch_schema_ls, test_dispatch_schema_show
6 dispatch Validate arm D6 arm no-ops KILLED (new) test_dispatch_validate
7 dispatch SchemaCheck arm D7 arm no-ops KILLED (new) test_dispatch_schema_check
8 dispatch Generate arm D8 arm no-ops KILLED (new) test_dispatch_generate
9 Error Display fixed strings E1 "corrupt meta" mutated KILLED (new) test_display_fixed_strings
10 Error Display formatted wrappers E2 InvalidInput drops value KILLED (new) test_display_formatted_wrappers
11 Error Display InvalidMetaMagic two-field format E3 expected/actual swapped KILLED (new) test_display_invalid_meta_magic_field_order
12 From impls route to variants E4 serde_json → InvalidInput KILLED (pre-existing) meta::types::solidity_abi::v2::tests::test_all
13 From impls route to variants E5 Utf8Error → InvalidInput KILLED (new) test_from_utf8_error_routes_to_utf8_error
14 query POSTs to base url MB01 POST→GET KILLED (pre-existing) all six metaboard httpmock tests
15 query errors Some → GraphqlError MB02 errors ignored KILLED (new) test_get_metabytes_by_hash_graphql_error
16 query data None → Empty MB03 Empty→GraphqlError SURVIVED (equivalent: dead arm, see #178) pinned instead by test_get_metabytes_by_hash_null_data_is_request_decode_error
17 query reqwest error → Request MB04 mapped to Empty KILLED (new) test_get_metabytes_by_hash_connection_error_is_request
18 by_hash 0x-prefixed hex variable MB05 prefix dropped KILLED (strengthened) test_get_metabytes_by_hash_success
19 by_hash empty → Empty(hash) MB06 guard removed KILLED (pre-existing) test_get_metabytes_by_hash_empty
20 by_hash Empty payload is the hash MB07 payload blanked KILLED (strengthened) test_get_metabytes_by_hash_empty
21 by_hash hex decode → FromHexError MB08 silently dropped KILLED (new) test_get_metabytes_by_hash_invalid_hex_meta
22 by_subject subject verbatim MB09 0x stripped KILLED (pre-existing) test_get_metabytes_by_subject_success
23 by_subject Empty(subject) MB10 payload blanked KILLED (strengthened) test_get_metabytes_by_subject_empty
24 by_subject FromHexError keyed by meta_hash MB11 silently dropped KILLED (new) test_get_metabytes_by_subject_invalid_hex_meta (key encoding disputed: #177)
25 addresses first/skip variables MB12 swapped KILLED (strengthened) test_get_metaboard_addresses_success
26 address parse → AddressParseError MB13 silently defaulted KILLED (new) test_get_metaboard_addresses_invalid_address
27 MetasByHash where-filter MB14 filter field → subject KILLED (strengthened) test_get_metabytes_by_hash_success
28 MetasBySubject where-filter MB15 filter field → metaHash KILLED (strengthened) test_get_metabytes_by_subject_success
29 MetaBoardAddresses first/skip args MB16 swapped KILLED (strengthened) test_get_metaboard_addresses_success
30 metaboard build.rs schema registration — UNPROBED compile-time guarantee; any mutation is a build failure (NO-RUN), not a runtime behaviour
31 bindings sol! codegen from committed ABIs — UNPROBED excluded by design: generated bindings from committed data; mutating the JSONs mutates fixtures shared with the oracle
32 isRainMetaV1 strict < 8 guard (8 proceeds) S01 <=8 KILLED (pre-existing) testEmitMetaEmptyBody, testIsRainMetaV1_2Fuzz
33 isRainMetaV1 strict < 8 guard (7 rejected) S02 <7 KILLED (new) testIsRainMetaV1_2LengthSevenMagicByteBeyond
34 isRainMetaV1 uint64 mask S03 uint56 / S04 uint72 KILLED (pre-existing) testIsRainMetaV1_2Fuzz
35 isRainMetaV1 load offset meta+8 S05 meta+7 KILLED (pre-existing) testIsRainMetaV1_2Fuzz
36 isRainMetaV1 equality vs META_MAGIC_NUMBER_V1 S06 magic+1 KILLED (pre-existing) testIsRainMetaV1_2Fuzz
37 magic constant literal 0xff0a89c674ee7874 S07 literal+1 KILLED (new) testIsRainMetaV1_2MagicNumberLiteral (survived the whole pre-existing suite: tests co-varied with the constant)
38 isRainMetaV1 true only on exact prefix S08 return true KILLED (pre-existing) testIsRainMetaV1_2Fuzz
39 checkMetaUnhashedV1 no-op on rain meta S09 guard inverted KILLED (pre-existing) testCheckMetaUnhashedV1_2Fuzz
40 checkMetaUnhashedV1 revert carries exact bytes S10 payload emptied KILLED (pre-existing) testCheckMetaUnhashedV1_2Fuzz
41 checkMetaHashedV1 hashes exactly the meta bytes S11 offset+1 KILLED (pre-existing) testCheckMetaHashedV1_2Happy
42 UnexpectedMetaHash(expected, actual) order S12 args swapped KILLED (pre-existing) testCheckMetaHashedV1_2GoodMagicBadHash
43 hash checked BEFORE magic S13 order swapped KILLED (pre-existing) testCheckMetaHashedV1_2BadMagicBadHash
44 fallthrough to checkMetaUnhashedV1 S14 call removed KILLED (pre-existing) testCheckMetaHashedV1_2BadMagicGoodHash
45 happy path no revert S15 comparison inverted KILLED (pre-existing) testCheckMetaHashedV1_2Happy
46 emitMeta guard S16 check removed KILLED (pre-existing) testEmitMetaNotRainMeta
47 emitMeta exactly one event S17 emitted twice KILLED (pre-existing) testEmitMetaHappy (log count)
48 emitMeta emitter = inlining contract — UNPROBED no syntactic mutant exists: the emitter is EVM emit semantics of the inlined call context; asserted by testEmitMetaHappy (emitter check) but unfalsifiable by source mutation
49 emitMeta sender = msg.sender S18 address(this) / S19 tx.origin KILLED (pre-existing) testEmitMetaHappy
50 emitMeta subject verbatim S20 zeroed KILLED (pre-existing) testEmitMetaHappy
51 emitMeta meta verbatim S21 emptied KILLED (pre-existing) testEmitMetaHappy
52 MetaV1_2 topic0 + exactly one topic S22 sender indexed KILLED (pre-existing) testEmitMetaHappy (topic count; testArtifactsCommitted co-fails on the ABI change)
53 emitForDescribedAddress fetches expected hash externally S23 computed locally / S24 zeroed KILLED (pre-existing) testEmitForDescribedAddressMismatch, testEmitForDescribedAddressHappy

QA

  • Discriminating tests: cli_dispatch.rs (test_dispatch_schema_ls/_schema_show/_validate/_schema_check/_magic_ls/_build/_solc_artifact/_subgraph/_generate), error::tests (test_display_fixed_strings, test_display_formatted_wrappers, test_display_invalid_meta_magic_field_order, test_from_serde_json_routes_to_serde_json_error, test_from_utf8_error_routes_to_utf8_error, test_from_from_utf8_error_routes_to_from_utf8_error), metaboard_client::tests (graphql_error, null_data_is_request_decode_error, connection_error_is_request, invalid_hex_meta x2, invalid_address, plus strengthened success/empty pins), testIsRainMetaV1_2LengthSevenMagicByteBeyond, testIsRainMetaV1_2MagicNumberLiteral — each proven to fail on base by the mutation probe: every one is the named killer of at least one mutant that SURVIVED the pre-change suite in the attribution pass (probe verdict lines below), and all are green on the unmutated branch (cargo 158 passed, forge 18 passed).

  • Oracle: expected values derived independently of the implementation — the metadata-v1 spec magic literal 0xff0a89c674ee7874 (cross-pinned against crates/cli/src/meta/magic.rs), the subgraph GraphQL schema for wire-shape pins (where: {metaHash: …}, first/skip; rendering verified against cynic output, not against the client code), process-level observables of the compiled binary (exit codes, exact stdout lines), and EVM event/ABI semantics for the Solidity units; Display strings are the user-facing contract pinned verbatim.

  • Category check: AMT run for group g6-cli-plumbing-output-enco — behaviours asked: cli dispatch 8 arms; error Display+From; metaboard CynicClient+client paths+fragments+build.rs; bindings codegen (excluded by design); LibMeta isRainMetaV1/checkMetaUnhashedV1/checkMetaHashedV1; LibIMetaBoardV1_2.emitMeta; LibDescribedByMeta external fetch. All covered in the matrix above; the two UNPROBED rows (compile-time build.rs, codegen-only bindings, emit-context emitter) carry their justification inline.

  • Mutations applied: full mutant list in the matrix above (file → exact-string mutation → killing test). Attribution pass (no new tests present, commit bba50a7 pristine): 24 Solidity mutants → 22 KILLED by named pre-existing tests, 2 SURVIVED (S02, S07). 29 Rust mutants → 4 KILLED by pre-existing tests (E4, MB01, MB06, MB09), 25 SURVIVED. Probe outputs: batches over mutation-probe <mutants>.toml --only …, JSON reports retained in the run scratchpad.

  • Final pass (this branch, full suites cargo test = 158 passed / forge test = 18 passed as baseline): all 24 Solidity mutants KILLED; 28/29 Rust mutants KILLED; MB03 SURVIVED and is recorded as an equivalent mutant — the mutated arm is dead code (cynic's GraphQlResponse deserializer rejects bodies without data or errors, so data: None with no errors is unrepresentable). Filed as CynicClientError::Empty is unreachable from CynicClient::query: missing/null data surfaces as a Request decode error #178 with a pinning test for the actual observable.

  • No NO-RUN, no HARNESS-ERROR in the final pass.

Second verification pass (independent mutant derivation)

A fresh session re-derived the mutant set from the behaviour list without reusing the first pass's mutants file (28 Solidity + 42 Rust exact-string mutants) and ran the probe twice: an attribution pass on pristine bba50a7 (Solidity lane: 26/28 killed by named pre-existing tests; the two survivors — length-guard removal and the magic constant literal — are exactly the gaps the first pass's testIsRainMetaV1_2LengthSevenMagicByteBeyond / testIsRainMetaV1_2MagicNumberLiteral were written for), then a full pass against this branch.

Branch results: Solidity 28/28 KILLED. Rust 36/42 KILLED, 1 survivor is the documented equivalent (data: None dead arm, #178), and 5 survivors were real residual gaps in unit-note targets the first pass had not discriminated:

Behaviour (unit-note target) Mutation Verdict Killing test
output() honours output_path (writes the file, not stdout) output_path ignored, bytes to stdout KILLED (strengthened) test_dispatch_solc_artifact (-o: exact file content + empty stdout)
of_chain(1) returns the ethereum set 1 => POLYGON KILLED (strengthened) test_dispatch_subgraph (exact 3 ethereum URLs)
of_chain(80001) returns the mumbai set 80001 => UnsupportedNetwork KILLED (strengthened) test_dispatch_subgraph (exact 3 mumbai URLs)
extract bytecode arm is its own component bytecode => deployedBytecode KILLED (strengthened) test_dispatch_solc_artifact (-c bytecode == "0x60")
schema show op-v1 returns the OpMeta schema OpV1 => AuthoringMeta schema KILLED (strengthened) test_dispatch_schema_show (title == "OpMeta.")

All five were killed by strengthening the existing dispatch tests in place; the second-pass re-probe with --only over these five reports all KILLED, and the full cli_dispatch suite is green on the unmutated branch.

Issues filed (audit + adversarial)

#177, #178, #179, #180, #181, #182, #184.

🤖 Generated with Claude Code

Adversarial mutation-test coverage for the g6 slice:

- crates/cli/tests/cli_dispatch.rs: end-to-end discrimination of all 8
  dispatch arms through the compiled binary (schema ls/show, validate,
  schema-check, magic ls, build, solc artifact, subgraph, generate).
- crates/cli/src/error/mod.rs: Display pins (fixed strings, formatted
  wrappers, InvalidMetaMagic field order) and From-routing tests.
- crates/metaboard: strengthened wire-shape pins (0x-prefixed hash,
  where-filters, first/skip argument order and variables, Empty payloads)
  plus new tests for GraphqlError, Request transport errors, the
  deserializer boundary on null data, FromHexError and AddressParseError
  paths.
- test/lib/LibMeta.isRainMetaV1_2.t.sol: length-7 boundary probe with a
  magic byte just past the array end, and a magic-literal pin that cannot
  co-vary with the META_MAGIC_NUMBER_V1 constant.
- Fixed three pre-existing fuzz oracles that asserted raw fuzz data is
  never rain meta; the fuzzer's counterexample 0xff0a89c674ee7874 (data
  that itself begins with the magic) falsifies that assumption while the
  library is correct per spec.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 22, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 58 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3c64e4e3-c909-4ca3-90b1-6bb5e31c1664

📥 Commits

Reviewing files that changed from the base of the PR and between b679138 and 9265c75.

📒 Files selected for processing (6)
  • crates/cli/src/error/mod.rs
  • crates/cli/tests/cli_dispatch.rs
  • crates/metaboard/src/metaboard_client.rs
  • test/lib/LibMeta.checkMetaHashedV1_2.t.sol
  • test/lib/LibMeta.checkMetaUnhashedV1_2.t.sol
  • test/lib/LibMeta.isRainMetaV1_2.t.sol

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thedavidmeister

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 22, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@thedavidmeister

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 22, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

baku-ccron and others added 2 commits August 22, 2026 22:12
An independently re-derived mutant set (28 Solidity + 42 Rust) probed
against the branch found five residual survivors in unit-note targets:

- output() with --output-path (file written, stdout silent)
- of_chain(1) ethereum set and of_chain(80001) mumbai set
- extract_artifact_component_json bytecode arm
- schema show op-v1 returning the OpMeta schema

Each is now discriminated by strengthening the existing dispatch tests in
place (exact URL sets, exact component values, exact file content, schema
title). Probe re-run with --only over the five: 5/5 KILLED; full suite
green on the unmutated branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
main and this branch each fixed the same two fuzz tests: main by
assuming/forcing the non-prefix case via LibMeta.isRainMetaV1, this
branch by asserting both branches against an independently derived
prefix check. Kept this branch's form — the oracle must not be the
function under mutation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@thedavidmeister
thedavidmeister merged commit 82ae439 into main Aug 25, 2026
10 of 11 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

@coderabbitai assess this PR size classification for the totality of the PR with the following criterias and report it in your comment:

S/M/L PR Classification Guidelines:

This guide helps classify merged pull requests by effort and complexity rather than just line count. The goal is to assess the difficulty and scope of changes after they have been completed.

Small (S)

Characteristics:

  • Simple bug fixes, typos, or minor refactoring
  • Single-purpose changes affecting 1-2 files
  • Documentation updates
  • Configuration tweaks
  • Changes that require minimal context to review

Review Effort: Would have taken 5-10 minutes

Examples:

  • Fix typo in variable name
  • Update README with new instructions
  • Adjust configuration values
  • Simple one-line bug fixes
  • Import statement cleanup

Medium (M)

Characteristics:

  • Feature additions or enhancements
  • Refactoring that touches multiple files but maintains existing behavior
  • Breaking changes with backward compatibility
  • Changes requiring some domain knowledge to review

Review Effort: Would have taken 15-30 minutes

Examples:

  • Add new feature or component
  • Refactor common utility functions
  • Update dependencies with minor breaking changes
  • Add new component with tests
  • Performance optimizations
  • More complex bug fixes

Large (L)

Characteristics:

  • Major feature implementations
  • Breaking changes or API redesigns
  • Complex refactoring across multiple modules
  • New architectural patterns or significant design changes
  • Changes requiring deep context and multiple review rounds

Review Effort: Would have taken 45+ minutes

Examples:

  • Complete new feature with frontend/backend changes
  • Protocol upgrades or breaking changes
  • Major architectural refactoring
  • Framework or technology upgrades

Additional Factors to Consider

When deciding between sizes, also consider:

  • Test coverage impact: More comprehensive test changes lean toward larger classification
  • Risk level: Changes to critical systems bump up a size category
  • Team familiarity: Novel patterns or technologies increase complexity

Notes:

  • the assessment must be for the totality of the PR, that means comparing the base branch to the last commit of the PR
  • the assessment output must be exactly one of: S, M or L (single-line comment) in format of: SIZE={S/M/L}
  • do not include any additional text, only the size classification
  • your assessment comment must not include tips or additional sections
  • do NOT tag me or anyone else on your comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant